package com.tianji.promotion.service.impl;

import com.tianji.api.dto.promotion.CouponDiscountDTO;
import com.tianji.api.dto.trade.OrderCourseDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.enums.DiscountType;
import com.tianji.promotion.service.DiscountService;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.IUserCouponService;
import com.tianji.promotion.strategy.discount.Discount;
import com.tianji.promotion.strategy.discount.DiscountStrategy;
import com.tianji.promotion.utils.PermuteUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DiscountServiceImpl implements DiscountService {

    @Autowired
    private IUserCouponService userCouponService;
    @Autowired
    private ICouponScopeService scopeService;

    @Override
    public List<CouponDiscountDTO> findDiscountSolution(List<OrderCourseDTO> orderCourses) {
        Long userId = UserContext.getUser();
        // 查询用户所有可用的优惠券
        List<Coupon> couponList = userCouponService.getAvailableByUserId(userId);
        if(CollUtils.isEmpty(couponList)){
            return new ArrayList<>();
        }
        // 初步筛选, 判断订单的总价和优惠券的门槛
        // 计算订单的总金额
        List<Coupon> availableCoupons = new ArrayList<>();
        int totalAmount = orderCourses.stream().mapToInt(OrderCourseDTO::getPrice).sum();
        for (Coupon coupon : couponList) {
            DiscountType discountType = DiscountType.of(coupon.getDiscountType());
            Discount discountStrategy = DiscountStrategy.getDiscount(discountType);
            boolean canUse = discountStrategy.canUse(totalAmount, coupon);
            if(canUse){
                availableCoupons.add(coupon);
            }
        }
        if(CollUtils.isEmpty(availableCoupons)){
            return new ArrayList<>();
        }
        // 细筛, 判断优惠券的使用范围
        // 记录coupon和能够应用到的商品的对应关系，key：Coupon  value：商品列表
        List<Coupon> remainCouponList = new ArrayList<>();
        Map<Coupon, List<OrderCourseDTO>> couponCourseMap = new HashMap<>();
        for (Coupon coupon: availableCoupons) {
            Boolean specific = coupon.getSpecific();
            if(!specific){
                remainCouponList.add(coupon);
                couponCourseMap.put(coupon, orderCourses);
            }else{
                // 找到券的应用范围
                List<CouponScope> scopes = scopeService.getByCouponId(coupon.getId());
                // 找到订单中符合范围的商品，求总价，判断是否能用
                // 符合范围的商品列表
                List<OrderCourseDTO> scopeCouseList = new ArrayList<>();
                for (OrderCourseDTO orderCourse : orderCourses) {
                    boolean present = scopes.stream().filter(s -> Objects.equals(s.getBizId(), orderCourse.getCateId())).findAny().isPresent();
                    if(present){
                        scopeCouseList.add(orderCourse);
                    }
                }
                if(CollUtils.isEmpty(scopeCouseList)){
                    continue;
                }
                int scopeCourseAmount = scopeCouseList.stream().mapToInt(OrderCourseDTO::getPrice).sum();
                DiscountType discountType = DiscountType.of(coupon.getDiscountType());
                Discount discountStrategy = DiscountStrategy.getDiscount(discountType);
                boolean canUse = discountStrategy.canUse(scopeCourseAmount, coupon);
                if(canUse){
                    couponCourseMap.put(coupon, scopeCouseList);
                    remainCouponList.add(coupon);
                }
            }
        }
        if(CollUtils.isEmpty(remainCouponList)){
            return new ArrayList<>();
        }
        // 优惠方案列表
        List<List<Coupon>> solutionList = null;
        // 全排列
        if(remainCouponList.size() <= 1){
            solutionList.add(Arrays.asList(remainCouponList.get(0)));
        }else{
            solutionList = PermuteUtil.permute(remainCouponList);
            for (Coupon coupon : remainCouponList) {
                solutionList.add(Arrays.asList(coupon));
            }
        }
        List<CouponDiscountDTO> discountDTOList = new ArrayList<>();
        // 计算每一种方案的优惠明细
        for (List<Coupon> solution : solutionList) {
            // 某一个优惠方案的优惠结果
            CouponDiscountDTO discountDTO = new CouponDiscountDTO();
            // 保存优惠券以及总的优惠金额
            Map<Coupon, Integer> discountMap = new LinkedHashMap<>();
            // 每个商品的分摊金额
            Map<Long, Integer> courseDiscountMap = new HashMap<>();
            for (Coupon coupon : solution) {
                // 找到当前这个券能应用到的商品列表
                List<OrderCourseDTO> couponCourseList = couponCourseMap.get(coupon);
                // 计算范围内的商品的总价
                int couponCourseAmount = couponCourseList.stream().mapToInt((course)->{
                    // 商品的剩余金额  = 商品金额 - 商品的分摊金额
                    return course.getPrice() - courseDiscountMap.getOrDefault(course.getId(), 0);
                }).sum();
                DiscountType discountType = DiscountType.of(coupon.getDiscountType());
                Discount discountStrategy = DiscountStrategy.getDiscount(discountType);
                // 判断是否能用这个券
                if(!discountStrategy.canUse(couponCourseAmount, coupon)){
                    continue;
                }
                // 优惠金额
                int discountAmount = discountStrategy.calculateDiscount(couponCourseAmount, coupon);
                // 计算优惠的分摊  某个商品的分摊金额 = 商品金额 / 商品的总价  * discountAmount；
                for (OrderCourseDTO orderCourseDTO : couponCourseList) {
                    double rate = (orderCourseDTO.getPrice()-courseDiscountMap.getOrDefault(orderCourseDTO.getId(), 0)) * 1.0 / couponCourseAmount;
                    int courseDiscount =  (int)(rate * discountAmount);
                    courseDiscountMap.put(orderCourseDTO.getId(), courseDiscount + courseDiscountMap.getOrDefault(orderCourseDTO.getId(), 0));
                }
                discountMap.put(coupon, discountAmount+discountMap.getOrDefault(coupon, 0));
            }
            // 组装结果对象
            List<Long> idList = new ArrayList<>();
            List<String> ruleList = new ArrayList<>();
            int amount = 0;
            for (Coupon coupon : discountMap.keySet()) {
                idList.add(coupon.getId());
                DiscountType discountType = DiscountType.of(coupon.getDiscountType());
                Discount discount = DiscountStrategy.getDiscount(discountType);
                ruleList.add(discount.getRule(coupon));
                amount += discountMap.get(coupon);
            }
            discountDTO.setIds(idList);
            discountDTO.setRules(ruleList);
            discountDTO.setDiscountAmount(amount);
            discountDTOList.add(discountDTO);
        }
        // 筛选最优解, - 用券相同时，优惠金额最高的方案, 优惠金额相同时，用券最少的方案
        Map<String, CouponDiscountDTO> moreDiscountMap = new HashMap<>();
        Map<Integer, CouponDiscountDTO> lessCouponMap = new HashMap<>();
        for (CouponDiscountDTO discountDTO : discountDTOList) {
            // 用券相同时，优惠金额最高的方案
            String ids = discountDTO.getIds().stream().sorted().map(String::valueOf).collect(Collectors.joining(","));
            CouponDiscountDTO bestAmount = moreDiscountMap.get(ids);
            if(bestAmount == null){
                bestAmount = discountDTO;
                moreDiscountMap.put(ids, bestAmount);
            }else{
                if(discountDTO.getDiscountAmount() > bestAmount.getDiscountAmount()){
                    bestAmount = discountDTO;
                    moreDiscountMap.put(ids, bestAmount);
                }
            }
            // 优惠金额相同时，用券最少的方案
            CouponDiscountDTO lestCoupon = lessCouponMap.get(discountDTO.getDiscountAmount());
            if(lestCoupon == null){
                lestCoupon = discountDTO;
                lessCouponMap.put(discountDTO.getDiscountAmount(), lestCoupon);
            }else{
                if(discountDTO.getIds().size() < lestCoupon.getIds().size()){
                    lestCoupon = discountDTO;
                    lessCouponMap.put(discountDTO.getDiscountAmount(), lestCoupon);
                }
            }
        }
        // 求交集
        Collection<CouponDiscountDTO> bestSolutions = CollUtils
                .intersection(moreDiscountMap.values(), lessCouponMap.values());
        // 按照优惠金额排序
        List<CouponDiscountDTO> list = bestSolutions.stream()
                .sorted(Comparator.comparingInt(CouponDiscountDTO::getDiscountAmount).reversed())
                .collect(Collectors.toList());
        return list;
    }
}
